/************************************************************************/
/*    Author: Scott Bevin  
-     Chris, i've made some modifications*/
/************************************************************************/

#include "GameEntityManager.h"
#include "..\XUtilities\Utilities.h"
#include "../XEntityComponents\EntityComponentFactory.h"
#include "../XEntityComponents\EntityComponent.h"
#include "../XUtilities\Logger.h"
#include "../XSystemComponents\XDebugging\DebugVariableTracker.h"
#include "../XPropertys\PropertyFactory.h"
#include "../XUtilities/ScriptParser.h"

namespace XF
{
	GameEntityManager* GameEntityManager::smInstance = NULL;

	GameEntityManager::GameEntityManager()
	{
		mNewGameEntity = NULL;
		mPauseEntities = false;
	}

	GameEntityManager::~GameEntityManager()
	{
		delete mGameEnityLog;
		ClearCache();
	}

	GameEntity* GameEntityManager::LookUpGameEntity(std::string fileName)
	{
		fileName = XF::Utilities::ToLower(fileName);

		if(mEntityCache.find(fileName) == mEntityCache.end())
		{
			//creating one will put it in the entity cache
			
			GameEntity* ge = CreateGameEntity(fileName);

			if(ge)
			{		
				ge->Shutdown();
				delete ge;
				mAliveEntitys.pop_back();
			}
			else
			{
				ILOG << "Tried to create an entity that doesnt exist";
			}
		}

		GameEntity* ret = mEntityCache[fileName];

		return ret;
	}

	void GameEntityManager::ClearCache()
	{
		ILOG << "XF::GameEntityManager - cleared cache!";

		for(std::map<std::string, GameEntity*>::iterator it = mEntityCache.begin();
			it != mEntityCache.end(); ++it)
		{
			delete it->second;
		}

		mEntityCache.clear();
	}

	bool GameEntityManager::PreloadAllEntityScripts()
	{
		std::map<std::string, std::string>::iterator it;

		for(it = mFilepathMap.begin(); it != mFilepathMap.end(); ++it)
		{
			GameEntity* ge = CreateGameEntity(it->first);
	
			ge->Shutdown();
			delete ge;
			mAliveEntitys.pop_back();
		}

		return true;
	}

	bool GameEntityManager::Initialize(std::string configFilePath)
	{
		ScriptParser fileParser;

		
		smInstance = new GameEntityManager();

		if(!fileParser.ParseFile(configFilePath,
			ScriptParser::ConfigScriptCallback::FromMethod<GameEntityManager, &GameEntityManager::ConfigFileParserCallback>(smInstance)))
		{
			SSELOG << "XF::GameEntityManager - Failed to initialize.";
			delete smInstance;
			smInstance = NULL;
		}
		else
		{
			SSELOG << "XF::GameEntityFactoy - succesfully initialized.";
		}

		return (smInstance != NULL);
	}

	GameEntity* GameEntityManager::CreateGameEntity(std::string fileName)
	{
		if(mNewGameEntity)
		{
			delete mNewGameEntity;
			mNewGameEntity = NULL;
		}

		fileName = XF::Utilities::ToLower(fileName);

		std::map<std::string, GameEntity*>::iterator it = mEntityCache.find(fileName);

		if(it != mEntityCache.end())
		{
			mNewGameEntity = new GameEntity(*mEntityCache[fileName]);
		}
		else
		{
			mNewGameEntity = new GameEntity();

			std::string filePath = mFilepathMap[fileName];

			XF::ScriptParser fileParser;


			DebugMessage(mGameEnityLog) << "loading script: "<<filePath;
			if(!fileParser.ParseFile(filePath, 
				XF::ScriptParser::ConfigScriptCallback::FromMethod<GameEntityManager, &GameEntityManager::FileParserCallback>(this)))
			{
				ILOG << "XF::GameEntityManager - Failed to parse script file: " << filePath;

				delete mNewGameEntity;
				mNewGameEntity = NULL;
				return NULL;
			}

			mEntityCache.insert(std::make_pair(fileName, mNewGameEntity));

			mNewGameEntity = new GameEntity(*mNewGameEntity);			
		}

		mNewGameEntity->AddProperty(PropertyFactory::CreateProperty("string", "ScriptFile", fileName)); //chris, for error messages

		GameEntity* ret = mNewGameEntity;

		ret->Initialize();

		mAliveEntitys.push_back(ret);
		mNewGameEntity = NULL;

		return ret;
	}

	
	void GameEntityManager::ConfigFileParserCallback(std::string key, std::vector<std::string> &arguments)//I re-wrote this function so that it can recurse folders getting scripts by extension successfully chris
	{
		std::string fileDir = key + " " + XF::Utilities::JoinStrings(arguments);


		std::vector<std::string> vec;
		if(!XF::Utilities::GetFileNamesWithExtension(ENTITY_FILE_EXTENSION, fileDir,vec,true,true)) 
		{
			SSELOG << "Error getting file names "; 
		}

		for(int i = 0; i < (int)vec.size(); ++i)
		{
			std::string fileName = XF::Utilities::ToLower(vec[i]); //chris, just so that everything is stored in lower case 
			//std::string key = XF::Utilities::RemoveExtensionAndFolders(fileName);
			std::string::size_type folderPos = fileName.find_last_of("\\");
			std::string withoutFolders = fileName.substr(folderPos+1,fileName.size());
			std::string key = withoutFolders;
			if(mFilepathMap.find(key) == mFilepathMap.end())
			{
				if(fileName.empty())
				{
					ILOG << "empty file name";
				}
				else
				mFilepathMap.insert(std::make_pair(key, fileDir + "/"+fileName));
			}
			else
			{
				ILOG << "Duplicate file name found, file exists here: " << mFilepathMap[key] << " and here: " << vec[i] << " please rename one of them";
			}
			

		}
	}

	void GameEntityManager::FileParserCallback(std::string key, std::vector<std::string> &arguments)
	{
		XF::DebugMessage(mGameEnityLog) << "Reading line: " << key << " " << XF::Utilities::JoinStrings(arguments);
		if(key == "component")
		{
			XF::EntityComponentFactory factory;
			XF::EntityComponent *newComponent = factory.CreateComponent(arguments[0], mNewGameEntity);

			std::string variables = "";

			for(unsigned int i = 1; i < arguments.size(); ++i) variables += arguments[i] + " ";

			ScriptParser::ReadInProperties(newComponent,variables);
			/*moved into function ^
			std::vector<std::string> vars;
						XF::Utilities::TokenizeString(variables, vars, ":");
			
						for(unsigned int i = 0; i < vars.size(); ++i)
						{
							std::vector<std::string> tokens;
							XF::Utilities::TokenizeString(vars[i], tokens);
			
							PropertySerializer::PropertyTypeId typeID = PropertySerializer::TypeIdFromString(tokens[0]);
			
							IProperty* newProp = PropertySerializer::CreateProperty(typeID, tokens[1]);
			
							std::string value = "";
			
							for(unsigned int i = 2; i < tokens.size(); ++i) value += tokens[i] + " ";
							value = value.substr(0, value.size() - 1);
			
							newProp->SetFromString(value);
							newComponent->AddProperty(newProp);
						}*/
			

			mNewGameEntity->AddComponent(newComponent);
		}

		else if(key == "property")
		{
			std::string value = "";

			for(unsigned int i = 2; i < arguments.size(); ++i) value += arguments[i] + " ";
			value = value.substr(0, value.size() - 1);

			IProperty* newProp = PropertyFactory::CreateProperty(arguments[0], arguments[1], value);

			mNewGameEntity->AddProperty(newProp);
		}
	}

	void GameEntityManager::KillAllEntitys()
	{
		for(int i = 0; i < (int)mAliveEntitys.size(); ++i)
		{
			mAliveEntitys[i]->Kill();
		}

		Update(0.0f);
	}

	void GameEntityManager::HandleInput( double dt, Input *input )
	{
		for(unsigned int i = 0; i < mAliveEntitys.size(); ++i)
		{
			XF::GameEntity* entity = mAliveEntitys[i];

			entity->HandleInput(dt, input);
		}
	}

	void GameEntityManager::Update( double dT )
	{
		if(mPauseEntities)return; 
		for(unsigned int i = 0; i < mAliveEntitys.size();)
		{
			XF::GameEntity* entity = mAliveEntitys[i];

			if(entity->IsDead())
			{
				mAliveEntitys[i] = mAliveEntitys[mAliveEntitys.size() - 1];
				mAliveEntitys.pop_back();

				//needs FIXING here:
				//entity->Shutdown();
				bool alreadyIndeadList = false;
			
				for(unsigned int i = 0; i < mDeadEntitys.size(); ++i)
				{
					if(mDeadEntitys[i] == entity)alreadyIndeadList = true;
				}
					
				if(!alreadyIndeadList)
				{
					
					mDeadEntitys.push_back(entity);
				}
				
				
				
			}		
			else if(entity->IsInitialized())
			{
				entity->Update(dT);
				++i;
			}
		}

		XF::DebugVariableTracker::UpdateVariable("Num entitys", Utilities::ToString(mAliveEntitys.size()));
	}

	void GameEntityManager::CleanupDeads()
	{
		for(unsigned int i = 0; i < mDeadEntitys.size(); ++i)
		{
			mDeadEntitys[i]->Shutdown(); // chris
			if(mDeadEntitys[i]->IsInitialized() && mDeadEntitys[i]->ID() >= 0) //chris tempfix
			delete mDeadEntitys[i];
		}

		mDeadEntitys.clear();
	}
}